use std::{sync::Arc, thread, time::Duration};

use chardev::{
    stdio::{chardevice_stdio_map, CharDeviceStdio},
    CharBackendImpl, CharDevice, CharDeviceSignal, CharPollStatus,
};
use dfs::{inode::DfsInode, seq_file::SeqFileCreateOptions};
use manage::{ManageReadlineStatus, SiminkManage, SiminkQuitCause, SiminkRequest};
use monitor::{Monitor, MonitorSignal};
use objectid::ObjectId;
use rcu::{rcu_register_thread, rcu_unregister_thread};

use super::MonitorFile;

struct DummyCharBackend;

impl CharBackendImpl for DummyCharBackend {
    fn backend_can_recv(&self) -> usize {
        unreachable!()
    }
    fn backend_receive(&self, _: &[u8]) {
        unreachable!()
    }

    fn backend_event(&self, _: chardev::CharDeviceEvent) {}
}

impl ObjectId for DummyCharBackend {
    fn compatible(&self) -> &'static str {
        "main"
    }

    fn id(&self) -> &str {
        "main"
    }
}

const STDIO_MAIN: &str = "main";

pub(super) fn stdio_init(mon: Arc<Monitor>, manage: Arc<SiminkManage>, mon_inode: Arc<DfsInode>) {
    manage.clone().push_worker(
        thread::Builder::new()
            .name("mon_stdio".into())
            .spawn(move || {
                rcu_register_thread();

                chardevice_stdio_map().register_char_backend(Arc::new(DummyCharBackend));
                chardevice_stdio_map().select_current(STDIO_MAIN).unwrap();
                SeqFileCreateOptions::new()
                    .read_write(true)
                    .name("stdio")
                    .create_seq_file(
                        mon_inode.clone(),
                        Arc::new(MonitorFile { map: chardevice_stdio_map() }),
                    )
                    .unwrap();

                let char_stdio = Arc::new(CharDeviceStdio::default());
                let mut char_device =
                    CharDevice::create(char_stdio.clone(), chardevice_stdio_map());
                chardevice_stdio_map().register_char_device_impl(char_stdio);
                char_device.set_monitor(STDIO_MAIN);
                char_device.switch_to_monitor();

                'out: loop {
                    char_device.update_current();

                    if char_device.current().eq(STDIO_MAIN) {
                        manage.readline_wait_run();

                        'inner1: loop {
                            let readline = mon.readline().unwrap();
                            match readline {
                                MonitorSignal::Success(rl) => {
                                    let res = mon.readline_exec(&rl, None);
                                    if let Err(e) = res {
                                        mon.print(e.to_string(), None);
                                    }
                                },
                                MonitorSignal::CtrlC | MonitorSignal::CtrlD => {
                                    manage.readline_notify_interrupted();
                                    break 'out;
                                },
                            }

                            if manage.readline_status() == ManageReadlineStatus::Wait
                                || char_device.current().ne(STDIO_MAIN)
                            {
                                break 'inner1;
                            }

                            // 注意:
                            //
                            // 这里不能使用 SiminkStatus::Quiting 来检测状态, 因为是我们请求退出, 现在还没设置到 Quiting 状态
                            if manage.simink_request_status() == SiminkRequest::ReqHostExit {
                                break 'out;
                            }
                        }
                    } else {
                        char_device.prepare().unwrap();
                        char_device.wait(Duration::from_secs(0)).unwrap();

                        'inner2: loop {
                            let status = char_device.poll(Duration::from_secs(0)).unwrap();
                            match status {
                                CharPollStatus::Ready => {
                                    let signal = char_device.handle().unwrap();
                                    match signal {
                                        CharDeviceSignal::None => {},
                                        CharDeviceSignal::ReqExit => {
                                            manage.request_simink_quit(
                                                SiminkQuitCause::HostMonitorInterrupted,
                                            );
                                            char_device.end(status).unwrap();
                                            break 'out;
                                        },
                                        CharDeviceSignal::ReqSwitch => {
                                            char_device.switch_to_monitor().unwrap();
                                            char_device.end(status).unwrap();
                                            break 'inner2;
                                        },
                                    }
                                },
                                _ => {
                                    unreachable!()
                                },
                            }
                        }
                    }
                }
                let _ = mon.sync_history();
                rcu_unregister_thread();
            })
            .unwrap(),
    );
}
